﻿using CommunicationCore.OpcUaClient;
using Opc.Ua;
using Opc.Ua.Client;
using SHRIS.MIS.Common.Util;
using SHRIS.MIS.Common.Util.Model;
using SHRIS.MIS.Data.Repository;
using SHRIS.MIS.Entity.ScadaBaseInfo;
using SHRIS.MIS.IService.IOpcService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SHRIS.MIS.Service.OpcService
{

    public class KepwareService : RepositoryFactory,IKepwareService
    {
        public KepwareService()
        {
            if (GlobalContext.KepwareServerConfiguration == null)
            {
                throw new ArgumentNullException(nameof(GlobalContext.KepwareServerConfiguration));
            }

            if(GlobalContext.KepwareServerConfiguration.ServerName == OpcServerName)
            {
                m_ServerConfig = GlobalContext.KepwareServerConfiguration;
            }

            if (m_ServerConfig == null)
            {
                throw new ArgumentNullException(nameof(m_ServerConfig));
            }

            opcClient = new OpcUaSimpleClient(m_ServerConfig.ClientName,
                m_ServerConfig.UseConfiguration,
                m_ServerConfig.ConfigurationSectionName,
                m_ServerConfig.UserName,
                m_ServerConfig.Password,
                m_ServerConfig.SecerityPolic
                );

        }

        private const string OpcServerName = "TestKepware";
        private OpcUaSimpleClient opcClient;
        private KepwareServerConfiguration m_ServerConfig;


        public async Task Connect()
        {
            Console.WriteLine(m_ServerConfig.ServerUrl);
            try
            {
                await opcClient.Connect(m_ServerConfig.ServerUrl);
            }
            catch (Exception ex)
            {
                Console.WriteLine("DEBUG: connect failed  " + ex.Message);
            }
        }


        public void DoConnect()
        {
            try
            {
                opcClient.Connect(m_ServerConfig.ServerUrl).Wait();
                Console.WriteLine("DEBUG: connect complete");
            }
            catch (Exception ex)
            {
                Console.WriteLine("DEBUG: connect failed  " + ex.Message);
            }


        }

        public void BrowsNode()
        {
            List<string> dataAddress = new List<string>();
            List<string> dataTag = new List<string>();
            List<int> dataType = new List<int>();

            //找出 用户自定义通道
            ReferenceDescriptionCollection channels = opcClient.FoundUserDefineChannels();

            foreach (var c in channels)
            {
                //Console.WriteLine(c.DisplayName.Text);
                ReferenceDescriptionCollection devices = opcClient.FoundUserDefineNodes(c);

                foreach (var d in devices)
                {
                    //Console.WriteLine(" -" + d.DisplayName.Text);
                    ReferenceDescriptionCollection variables = opcClient.FoundUserDefineVariables(d);
                    foreach (var v in variables)
                    {
                        int type = opcClient.GetVariableDataType(v, Attributes.DataType);
                        dataAddress.Add(v.NodeId.ToString());
                        dataTag.Add(v.DisplayName.Text);
                        dataType.Add(type);


                    }

                }

            }


        }

        public async Task CreateSubscription()
        {
            //CS-D01-S-YYJ006 压力机
            // 状态 1 报警 5

            //CS-A11-S-NJJ001  拧紧机
            // 状态1 报警 1 工艺参数 2

            //CS-B01-S-DLQZJ001  起重机
            // 状态1 

            //获取服务器信息
            var server = await GetServerIdByName(m_ServerConfig.ServerName);

            if(server==null)
            {
                Console.WriteLine("DEBUG: CreateSubscription failed:no server information");
                return;
            }

            //获取该服务器的所有Channel
            var channels = await GetChannelsByServerId(server.Id);

            if (channels == null)
            {
                Console.WriteLine("DEBUG: CreateSubscription failed:no channel information");
                return;
            }

            foreach (var c in channels)
            {
                var datas = await GetDataSourceByChannel(server.Id,c.DataChannel);
                List<string> nodeId;
                List<string> nodeTag;
                GetMonitorAddress(datas, out nodeId, out nodeTag);
                opcClient.CreateSubscription(
                    c.DataChannel,
                    2000,
                    2000,
                    nodeId,
                    nodeTag,
                    RecordDataHandler
                    );
            }
        }

        private void RecordDataHandler(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
        {
            try
            {
                MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
                if (notification != null)
                {
                    SaveRowData(monitoredItem.DisplayName, notification.Value.Value.ToString(), notification.Value.ServerTimestamp);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void Disconnect()
        {
            opcClient.Disconnect();
        }


        public int SaveRowData(string disPlayName, string value, DateTimeOffset timeStamp)
        {
            string sql = string.Format("insert into tb_rowData" +
                "({0},{1},{2},{3},{4})" +
                "values('{5}','{6}','{7}','{8}','{9}')",
                    "RecordId", "DataSourceId", "DataValue", "ServerTime", "UpdateTime",
                    Guid.NewGuid(), Guid.Parse(disPlayName),value, timeStamp, DateTimeOffset.Now);
            return this.BaseRepository().ExecuteBySql(sql).Result;

        }


        public async Task<ServerListEntity> GetServerIdByName(string serverName)
        {
            var serverLists = await this.BaseRepository().FindList<ServerListEntity>(sv => sv.ServerName == serverName);
            return serverLists.FirstOrDefault();
        }


        public async Task<IEnumerable<DataSourceEntity>> GetChannelsByServerId(Guid serverId)
        {
            var datas = await this.BaseRepository().FindList<DataSourceEntity>(data => data.ServerId == serverId);
            return datas.Distinct(new ChannelComparer());
        }

        public async Task<IEnumerable<DataSourceEntity>> GetDataSourceByChannel(Guid serverId, string channel)
        {
            var datas = await this.BaseRepository().FindList<DataSourceEntity>(data => data.ServerId == serverId&&data.DataChannel== channel);
            return datas;
        }

        public async Task<bool> WriteData<T>(string sourceAddress,T value)
        {
            bool result = await opcClient.WriteNodeAsync(sourceAddress, value);
            return result;
        }

        public async Task<bool> WriteDataCollection()
        {
            bool result = await opcClient.WriteNodeAsync("ns=2;s=SiemensTCP.PlatesWorkShop.ProcessData.TightenJob", 33);
            return result;
        }

        private void GetMonitorAddress(IEnumerable<DataSourceEntity> datas, out List<string> nodeId, out List<string> nodeTag)
        {
            nodeId = new List<string>();
            nodeTag = new List<string>();
            foreach (var d in datas)
            {
                if (d.DataAction == 4)
                {
                    nodeId.Add(d.SourceAddress);
                    nodeTag.Add(d.Id.ToString());
                }
            }
        }


    }

    public class ChannelComparer : EqualityComparer<DataSourceEntity>
    {
        public override bool Equals(DataSourceEntity s1, DataSourceEntity s2)
        {
            return s1.DataChannel == s2.DataChannel;
        }
        public override int GetHashCode(DataSourceEntity student)
        {
            return student.DataChannel.GetHashCode();
        }
    }
}
